﻿///////////////////////////////////////////////////////////////////////////////
///
/// ConvertExt.cs
///
/// (c)2017 Kim, Hyoun Woo
///
///////////////////////////////////////////////////////////////////////////////
using UnityEngine;
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

namespace DLC.Module.DataTable
{
    /// <summary>
    /// DataTable specific a helper class for various type converting. 
    /// 
    /// </summary>
    public partial class ConvertExt
    {
        static public object[] Split(string value, char delimiter = ',')
        {
            string str = value as string;

            // remove whitespace between each of element
            str = new string(str.ToCharArray()
                                .Where(ch => !Char.IsWhiteSpace(ch))
                                .ToArray());

            // remove ',', if it is found at the end.
            char[] charToTrim = { ',', ' ' };
            str = str.TrimEnd(charToTrim);

            // split by ','
            object[] temp = str.Split(delimiter);
            return temp;
        }

        /// <summary>
        /// Convert the given string to array of float. 
        /// Note the string should contain ',' to separate each of array element.
        /// </summary>
        public static float[] ToSingleArray(string value, char delimiter = ',')
        {
            object[] temp = Split(value, delimiter);
            float[] result = temp.Select(e => Convert.ChangeType(e, typeof(float)))
                                 .Select(e => (float)e).ToArray();
            //ERROR: InvalidCastException: Cannot cast from source type to destination type.
            //float[] result = temp.Select(e => (float)e).ToArray();
            return result;
        }
        /// <summary>
        /// Convert the given string to array of double. 
        /// </summary>
        public static double[] ToDoubleArray(string value, char delimiter = ',')
        {
            object[] temp = Split(value, delimiter);
            double[] result = temp.Select(e => Convert.ChangeType(e, typeof(double)))
                                  .Select(e => (double)e).ToArray();
            return result;
        }
        /// <summary>
        /// Convert the given string to array of short. 
        /// </summary>
        public static short[] ToInt16Array(string value, char delimiter = ',')
        {
            object[] temp = Split(value, delimiter);
            short[] result = temp.Select(e => Convert.ChangeType(e, typeof(short)))
                                 .Select(e => (short)e).ToArray();
            return result;
        }
        /// <summary>
        /// Convert the given string to array of int. 
        /// </summary>
        public static int[] ToInt32Array(string value, char delimiter = ',')
        {
            object[] temp = Split(value, delimiter);
            int[] result = temp.Select(e => Convert.ChangeType(e, typeof(int)))
                                .Select(e => (int)e).ToArray();
            return result;
        }
        /// <summary>
        /// Convert the given string to array of long. 
        /// </summary>
        public static long[] ToInt64Array(string value, char delimiter = ',')
        {
            object[] temp = Split(value, delimiter);
            long[] result = temp.Select(e => Convert.ChangeType(e, typeof(long)))
                                .Select(e => (long)e).ToArray();          
            return result;
        }
        /// <summary>
        /// Convert the given string to array of long. 
        /// </summary>
        public static string[] ToStringArray(string value, char delimiter = ',')
        {
            object[] temp = Split(value, delimiter);
            string[] result = temp.Select(e => Convert.ChangeType(e, typeof(string)))
                                .Select(e => (string)e).ToArray();
            return result;
        }

        public static Vector2 ToVector2(string value, char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiterVector);
            float[] result = temp.Select(e => Convert.ChangeType(e, typeof(float)))
                                 .Select(e => (float)e).ToArray();
            Vector2 v = Vector2.zero;
            if (result.Length >= 1)
            {
                v.x = result[0];
            }
            if (result.Length >= 2)
            {
                v.y = result[1];
            }
            return v;
        }
        public static Vector2Int ToVector2Int(string value, char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiterVector);
            int[] result = temp.Select(e => Convert.ChangeType(e, typeof(int)))
                                 .Select(e => (int)e).ToArray();
            Vector2Int v = Vector2Int.zero;
            if (result.Length >= 1)
            {
                v.x = result[0];
            }
            if (result.Length >= 2)
            {
                v.y = result[1];
            }
            return v;
        }
        public static Vector3 ToVector3(string value, char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiterVector);
            float[] result = temp.Select(e => Convert.ChangeType(e, typeof(float)))
                                 .Select(e => (float)e).ToArray();
            Vector3 v = Vector3.zero;
            if (result.Length >= 1)
            {
                v.x = result[0];
            }
            if (result.Length >= 2)
            {
                v.y = result[1];
            }
            if (result.Length >= 3)
            {
                v.z = result[2];
            }
            return v;
        }
        public static Vector3Int ToVector3Int(string value, char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiterVector);
            int[] result = temp.Select(e => Convert.ChangeType(e, typeof(int)))
                                 .Select(e => (int)e).ToArray();
            Vector3Int v = Vector3Int.zero;
            if (result.Length >= 1)
            {
                v.x = result[0];
            }
            if (result.Length >= 2)
            {
                v.y = result[1];
            }
            if (result.Length >= 3)
            {
                v.z = result[2];
            }
            return v;
        }
        public static Vector4 ToVector4(string value, char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiterVector);
            float[] result = temp.Select(e => Convert.ChangeType(e, typeof(float)))
                                 .Select(e => (float)e).ToArray();
            Vector4 v = Vector4.zero;
            if (result.Length >= 1)
            {
                v.x = result[0];
            }
            if (result.Length >= 2)
            {
                v.y = result[1];
            }
            if (result.Length >= 3)
            {
                v.z = result[2];
            }
            if (result.Length >= 4)
            {
                v.w = result[3];
            }
            return v;
        }
        public static Vector2[] ToVector2Array(string value, char delimiter = ';', char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiter);
            string[] result = temp.Select(e => Convert.ChangeType(e, typeof(string)))
                                 .Select(e => (string)e).ToArray();
            List<Vector2> vectors = new List<Vector2>();
            foreach(var r in result)
            {
                vectors.Add(ToVector2(r, delimiterVector));
            }
            return vectors.ToArray();
        }
        public static Vector2Int[] ToVector2IntArray(string value, char delimiter = ';', char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiter);
            string[] result = temp.Select(e => Convert.ChangeType(e, typeof(string)))
                                 .Select(e => (string)e).ToArray();
            List<Vector2Int> vectors = new List<Vector2Int>();
            foreach (var r in result)
            {
                vectors.Add(ToVector2Int(r, delimiterVector));
            }
            return vectors.ToArray();
        }
        public static Vector3[] ToVector3Array(string value, char delimiter = ';', char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiter);
            string[] result = temp.Select(e => Convert.ChangeType(e, typeof(string)))
                                 .Select(e => (string)e).ToArray();
            List<Vector3> vectors = new List<Vector3>();
            foreach (var r in result)
            {
                vectors.Add(ToVector3(r, delimiterVector));
            }
            return vectors.ToArray();
        }
        public static Vector3Int[] ToVector3IntArray(string value, char delimiter = ';', char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiter);
            string[] result = temp.Select(e => Convert.ChangeType(e, typeof(string)))
                                 .Select(e => (string)e).ToArray();
            List<Vector3Int> vectors = new List<Vector3Int>();
            foreach (var r in result)
            {
                vectors.Add(ToVector3Int(r, delimiterVector));
            }
            return vectors.ToArray();
        }
        public static Vector4[] ToVector4Array(string value, char delimiter = ';', char delimiterVector = ',')
        {
            object[] temp = Split(value, delimiter);
            string[] result = temp.Select(e => Convert.ChangeType(e, typeof(string)))
                                 .Select(e => (string)e).ToArray();
            List<Vector4> vectors = new List<Vector4>();
            foreach (var r in result)
            {
                vectors.Add(ToVector4(r, delimiterVector));
            }
            return vectors.ToArray();
        }
        public static object ConvertFromString(string data, Type t, DelimitersAttribute delimiterAttributes)
        {
            object value = null;

            if (t == typeof(float) || t == typeof(double) || t == typeof(short) || t == typeof(int) || t == typeof(long))
            {
                if (t == typeof(float))
                    value = Convert.ToSingle(data);
                if (t == typeof(double))
                    value = Convert.ToDouble(data);
                if (t == typeof(short))
                    value = Convert.ToInt16(data);
                if (t == typeof(int))
                    value = Convert.ToInt32(data);
                if (t == typeof(long))
                    value = Convert.ToInt64(data);
            }
            else if (t == typeof(string) || t.IsArray)
            {
                value = data;
            }
            else if (t == typeof(bool))
                value = Convert.ToBoolean(data);

            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                var nc = new NullableConverter(t);
                return nc.ConvertFrom(value);
            }

            if (t.IsEnum)
            {
                // for enum type, first get value by string then convert it to enum.
                value = data;
                return Enum.Parse(t, value.ToString(), true);
            }
            else if (t == typeof(Vector2) || t == typeof(Vector2Int) || t == typeof(Vector3) || t == typeof(Vector3Int) || t == typeof(Vector4))
            {
                char delimiter = ',';
                if (delimiterAttributes != null)
                {
                    string delimiters = delimiterAttributes.Delimiters;
                    try
                    {
                        delimiter = delimiters[0];
                    }
                    catch { }
                }
                if (t == typeof(Vector2))
                    return ToVector2(data, delimiter);
                if (t == typeof(Vector2Int))
                    return ToVector2Int(data, delimiter);
                if (t == typeof(Vector3))
                    return ToVector3(data, delimiter);
                if (t == typeof(Vector3Int))
                    return ToVector3Int(data, delimiter);
                if (t == typeof(Vector4))
                    return ToVector4(data, delimiter);
            }
            else if (t.IsArray)
            {
                char delimiter = ',';
                char delimiter1 = ';';
                if (delimiterAttributes != null)
                {
                    string delimiters = delimiterAttributes.Delimiters;
                    try
                    {
                        delimiter = delimiters[0];
                        delimiter1 = delimiters[1];
                    }
                    catch { }
                }
                if (t.GetElementType() == typeof(float))
                    return ToSingleArray((string)value, delimiter);
                if (t.GetElementType() == typeof(double))
                    return ToDoubleArray((string)value, delimiter);
                if (t.GetElementType() == typeof(short))
                    return ToInt16Array((string)value, delimiter);
                if (t.GetElementType() == typeof(int))
                    return ToInt32Array((string)value, delimiter);
                if (t.GetElementType() == typeof(long))
                    return ToInt64Array((string)value, delimiter);
                if (t.GetElementType() == typeof(string))
                    return ToStringArray((string)value, delimiter);
                if (t.GetElementType() == typeof(Vector2))
                    return ToVector2Array(data, delimiter1, delimiter);
                if (t.GetElementType() == typeof(Vector2Int))
                    return ToVector2IntArray(data, delimiter1, delimiter);
                if (t.GetElementType() == typeof(Vector3))
                    return ToVector3Array(data, delimiter1, delimiter);
                if (t.GetElementType() == typeof(Vector3Int))
                    return ToVector3IntArray(data, delimiter1, delimiter);
                if (t.GetElementType() == typeof(Vector4))
                    return ToVector4Array(data, delimiter1, delimiter);
            }

            // for all other types, convert its corresponding type.
            return Convert.ChangeType(value, t);
        }
    }
}